package com.heima.article.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.FreemarkerService;
import com.heima.common.constants.BehaviorConstants;
import com.heima.common.exception.CustomException;
import com.heima.common.redis.CacheService;
import com.heima.model.common.dtos.*;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.pojos.article.ApArticle;
import com.heima.model.common.pojos.article.ApArticleConfig;
import com.heima.model.common.pojos.article.ApArticleContent;
import com.heima.model.common.pojos.user.ApUser;
import com.heima.model.common.vos.ApArticleVo;
import com.heima.model.common.vos.ArticleCommnetVo;
import com.heima.model.mess.ArticleVisitStreamMess;
import com.heima.utils.common.DateUtils;
import com.heima.utils.thread.ApThreadLocalUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 陈辉
 * @data 2024 9:50
 */
@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;
    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private FreemarkerService freemarkerService;
    @Autowired
    private CacheService cacheService;

    /**
     * 加载文章信息
     *
     * @param homeDto 查询条件
     * @param type    1：加载更多 2：加载更新
     * @return
     */
    @Override
    public ResponseResult load(ArticleHomeDto homeDto, int type) {
        //1. 参数校验
        if (homeDto == null || (type != 1 && type != 2)) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2. 数据处理
        if (homeDto.getMaxBehotTime() == null) homeDto.setMaxBehotTime(new Date());
        if (homeDto.getMinBehotTime() == null) homeDto.setMinBehotTime(new Date());
        if (homeDto.getSize() < 1 || homeDto.getSize() > 50) homeDto.setSize(10);

        //3. 调用mapper查询对应数据
        List<ApArticle> res = apArticleMapper.load(homeDto, type);

        //4. 封装结果返回
        return ResponseResult.okResult(res);
    }

    /**
     * 保存或修改文章信息
     *
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveOrUpdateArticle(ArticleDto articleDto) {
        //1. 同步自媒体文章信息
        if (articleDto.getId() == null) {
            //保存
            //1. 保存文章基本信息：ap_article
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(articleDto, apArticle);
            apArticleMapper.insert(apArticle);
            articleDto.setId(apArticle.getId());

            //2. 创建文章默认配置：ap_article_config
            ApArticleConfig config = new ApArticleConfig(apArticle.getId());
            apArticleConfigMapper.insert(config);

            //3. 保存文章内容信息：ap_article_content
            ApArticleContent content = new ApArticleContent();
            content.setArticleId(apArticle.getId());
            content.setContent(articleDto.getContent());
            apArticleContentMapper.insert(content);
        } else {
            //修改
            //1. 修改文章基本信息：ap_article
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(articleDto, apArticle);
            apArticleMapper.updateById(apArticle);

            //2. 修改文章内容信息：ap_article_content
            ApArticleContent content = new ApArticleContent();
            content.setArticleId(apArticle.getId());
            content.setContent(articleDto.getContent());
            //update ap_article_content set ? ,? where article_id = ?
            apArticleContentMapper.update(content,
                    Wrappers.<ApArticleContent>lambdaUpdate().eq(ApArticleContent::getArticleId, content.getArticleId()));
        }

        //2. 采用异步调用，提升当前接口的性能，生成文章详情页： freemarker + minio
        freemarkerService.generateDetailHtml(articleDto);


        //将文章id返回
        return ResponseResult.okResult(String.valueOf(articleDto.getId()));
    }


    /**
     * 文章上下架
     * <p>
     * is_down: 0: 上架， 1：下架
     *
     * @param map enable 0：下架， 1：上架
     */
    @Override
    public void downOrUp(Map map) {
        boolean isDown = true;            // 假设要下架

        if ((int) map.get("enable") == 1) {
            isDown = false;
        }
        ApArticleConfig config = new ApArticleConfig();
        //config.setArticleId((long) map.get("article_id"));
        //update ap_article_config set is_down = ? where article_id = ?
        apArticleConfigMapper.update(config,Wrappers.<ApArticleConfig>lambdaUpdate()
                .set(ApArticleConfig::getIsDown,isDown)
                .eq(ApArticleConfig::getArticleId,config.getArticleId()));

    }



    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {

        //0.检查参数
        if (dto == null || dto.getArticleId() == null || dto.getAuthorId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //{ "isfollow": true, "islike": true,"isunlike": false,"iscollection": true }
        boolean isfollow = false, islike = false, isunlike = false, iscollection = false;

        Integer userId = ApThreadLocalUtil.get();
        if(userId != null){
            //喜欢行为
            String likeBehaviorJson = (String) cacheService.hGet(BehaviorConstants.LIKE_BEHAVIOR + dto.getArticleId().toString(), userId.toString());
            if(StringUtils.isNotBlank(likeBehaviorJson)){
                islike = true;
            }
            //不喜欢的行为
            String unLikeBehaviorJson = (String) cacheService.hGet(BehaviorConstants.UN_LIKE_BEHAVIOR + dto.getArticleId().toString(), userId.toString());
            if(StringUtils.isNotBlank(unLikeBehaviorJson)){
                isunlike = true;
            }
            //是否收藏
            String collctionJson = (String) cacheService.hGet(BehaviorConstants.COLLECTION_BEHAVIOR+userId,dto.getArticleId().toString());
            if(StringUtils.isNotBlank(collctionJson)){
                iscollection = true;
            }

            //是否关注
            Double score = cacheService.zScore(BehaviorConstants.APUSER_FOLLOW_RELATION + userId, dto.getAuthorId().toString());
            System.out.println(score);
            if(score != null){
                isfollow = true;
            }

        }

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("isfollow", isfollow);
        resultMap.put("islike", islike);
        resultMap.put("isunlike", isunlike);
        resultMap.put("iscollection", iscollection);

        return ResponseResult.okResult(resultMap);
    }



    /**
     * 更新文章行为数量
     * @param mess
     */
    private ApArticle updateArticle(ArticleVisitStreamMess mess) {
        ApArticle apArticle = getById(mess.getArticleId());
        apArticle.setCollection(apArticle.getCollection()==null?0:apArticle.getCollection()+mess.getCollect());
        apArticle.setComment(apArticle.getComment()==null?0:apArticle.getComment()+mess.getComment());
        apArticle.setLikes(apArticle.getLikes()==null?0:apArticle.getLikes()+mess.getLike());
        apArticle.setViews(apArticle.getViews()==null?0:apArticle.getViews()+mess.getView());
        updateById(apArticle);
        return apArticle;

    }


    /**
     * 图文统计统计
     * @param wmUserId
     * @param beginDate
     * @param endDate
     * @return
     */
    @Override
    public ResponseResult queryLikesAndConllections(Integer wmUserId, Date beginDate, Date endDate) {
        //处理一下当天的时间
        beginDate.setHours(0);
        beginDate.setMinutes(0);
        beginDate.setSeconds(0);

        endDate.setHours(23);
        endDate.setMinutes(59);
        endDate.setSeconds(59);

        Map map = apArticleMapper.queryLikesAndConllections(wmUserId, beginDate, endDate);
        return ResponseResult.okResult(map);
    }

    /**
     * 分页查询 图文统计
     * @param dto
     * @return
     */
    @Override
    public PageResponseResult newPage(StatisticsDto dto) {

        //类型转换
        Date beginDate = DateUtils.stringToDate(dto.getBeginDate());
        Date endDate = DateUtils.stringToDate(dto.getEndDate());

        //处理一下当天的时间
        beginDate.setHours(0);
        beginDate.setMinutes(0);
        beginDate.setSeconds(0);

        endDate.setHours(23);
        endDate.setMinutes(59);
        endDate.setSeconds(59);

        //检查参数
        dto.checkParam();
        //分页查询
        IPage page = new Page(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<ApArticle> lambdaQueryWrapper = Wrappers.<ApArticle>lambdaQuery()
                .eq(ApArticle::getAuthorId, dto.getWmUserId())
                .between(ApArticle::getPublishTime,beginDate, endDate)
                .select(ApArticle::getId,ApArticle::getTitle,ApArticle::getLikes,ApArticle::getCollection,ApArticle::getComment,ApArticle::getViews);

        lambdaQueryWrapper.orderByDesc(ApArticle::getPublishTime);

        page = page(page,lambdaQueryWrapper);

        PageResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),(int)page.getTotal());
        responseResult.setData(page.getRecords());

        return responseResult;
    }

    /**
     * 查询文章评论统计
     * @param dto
     * @return
     */
    @Override
    public PageResponseResult findNewsComments(ArticleCommentDto dto) {

        Integer currentPage = dto.getPage();
        dto.setPage((dto.getPage()-1)*dto.getSize());
        //2024-08-03 14:56:18  2024-08-04
        //处理一下当前的数据统计
        dto.setBeginDate(dto.getBeginDate() + " 00:00:00");
        dto.setEndDate(dto.getEndDate() + " 23:59:59");
        List<ArticleCommnetVo> list = apArticleMapper.findNewsComments(dto);
        int count = apArticleMapper.findNewsCommentsCount(dto);

        PageResponseResult responseResult = new PageResponseResult(currentPage,dto.getSize(),count);
        responseResult.setData(list);
        return responseResult;
    }

    /**
     * 更新文章的分值
     * @param mess
     */
    @Override
    public void updateScore(ArticleVisitStreamMess mess) {
        //1.更新文章的阅读、点赞、收藏、评论的数量
        ApArticle apArticle = updateArticle(mess);
    }

    /**
     * 首页加载热点文章
     *
     * @param homeDto
     * @param type
     * @return
     */
    @Override
    public ResponseResult loadHotArticle(ArticleHomeDto homeDto, int type) {
        //先查redis有没有热点文章数据：有：直接返回即可； 没有：走数据库原有逻辑
        String hotArticleJSON = cacheService.get("hot_article_" + homeDto.getTag());
        if (StringUtils.isNotEmpty(hotArticleJSON)){
            List<ApArticleVo> apArticleVoList = JSONArray.parseArray(hotArticleJSON, ApArticleVo.class);
            return ResponseResult.okResult(apArticleVoList);
        }

        //缓存没有：走数据库原有逻辑
        return load(homeDto,type);
    }

    /**
     * 计算热点文章
     */
    @Override
    public void computeHotArticle() {
        //1. 查询近5天的文章数据
        Date fiveDayAgo = new Date(System.currentTimeMillis() - (1000L * 60 * 60 * 24 * 5));
        List<ApArticle> list = apArticleMapper.load5Days(fiveDayAgo);

        //2. 给文章算分
        List<ApArticleVo> articleVoList = computeScore(list);

        //3. 计算每个频道的热点文章，取分值最高的前30篇文章，存入redis
        //3.1 提取出当前这些文章对应的频道id
        List<Integer> channelIds = articleVoList.stream().map(ApArticleVo::getChannelId).distinct().collect(Collectors.toList());
        //3.2 遍历频道
        channelIds.stream().forEach(channelId->{
            //收集每个频道的文章,并按分值排序：降序，降序后截取前30篇
            List<ApArticleVo> hotArticleInSameChannel = articleVoList.stream()
                    .filter(apArticleVo -> apArticleVo.getChannelId() == channelId)
                    .sorted((o1, o2) -> o2.getScore() - o1.getScore())
                    .limit(30)
                    .collect(Collectors.toList());

            //存入redis
            cacheService.set("hot_article_"+channelId, JSONArray.toJSONString(hotArticleInSameChannel));
        });

        //4. 计算推荐频道的热点文章，取分值最高的前30篇文章，存入redis
        List<ApArticleVo> hotArticleWithAllChannel = articleVoList.stream()
                .sorted((o1, o2) -> o2.getScore() - o1.getScore())
                .limit(30)
                .collect(Collectors.toList());

        //存入redis
        cacheService.set("hot_article___all__", JSONArray.toJSONString(hotArticleWithAllChannel));
    }

    //给文章算分
    private List<ApArticleVo> computeScore(List<ApArticle> list) {
        List<ApArticleVo> vos = new ArrayList<>();

        for (ApArticle apArticle : list) {
            //计算文章分值
            int score = 0;
            //阅读权重：1分
            Integer views = apArticle.getViews();
            if (views != null && views > 0){
                score += (views * 1);
            }
            //点赞权重：3分
            Integer likes = apArticle.getLikes();
            if (likes != null && likes > 0){
                score += (likes * 3);
            }
            //评论权重：5分
            Integer comment = apArticle.getComment();
            if (comment != null && comment > 0){
                score += (comment * 5);
            }
            //收藏权重：8分
            Integer collection = apArticle.getCollection();
            if (collection != null && collection > 0){
                score += (collection * 8);
            }

            ApArticleVo vo = new ApArticleVo();
            BeanUtils.copyProperties(apArticle,vo);
            vo.setScore(score);
            vos.add(vo);
        }

        return vos;
    }
}
